using System;
using UnityEngine;

namespace Voodoo.Utils.Ease
{
	public static class CurveEasing
	{
		public static AnimationCurve GenerateAnimationCurve(Ease easing)
		{
			switch (easing)
			{
			case Ease.EaseInQuad:
				return CreateInCurve(GetBezierKeyFrames(P(0.3333f, 0f), P(0.6666f, 0.3333f)));
			case Ease.EaseOutQuad:
				return CreateOutCurve(GetBezierKeyFrames(P(0.3333f, 0f), P(0.6666f, 0.3333f)));
			case Ease.EaseInOutQuad:
				return CreateInOutCurve(GetBezierKeyFrames(P(0.3333f, 0f), P(0.6666f, 0.3333f)));
			case Ease.EaseInCubic:
				return CreateInCurve(GetBezierKeyFrames(P(0.3333f, 0f), P(0.6666f, 0f)));
			case Ease.EaseOutCubic:
				return CreateOutCurve(GetBezierKeyFrames(P(0.3333f, 0f), P(0.6666f, 0f)));
			case Ease.EaseInOutCubic:
				return CreateInOutCurve(GetBezierKeyFrames(P(0.3333f, 0f), P(0.6666f, 0f)));
			case Ease.EaseInQuart:
				return CreateInCurve(GetInQuarticKeyframes());
			case Ease.EaseOutQuart:
				return CreateOutCurve(GetInQuarticKeyframes());
			case Ease.EaseInOutQuart:
				return CreateInOutCurve(GetInQuarticKeyframes());
			case Ease.EaseInQuint:
				return CreateInCurve(GetInQuinticKeyframes());
			case Ease.EaseOutQuint:
				return CreateOutCurve(GetInQuinticKeyframes());
			case Ease.EaseInOutQuint:
				return CreateInOutCurve(GetInQuinticKeyframes());
			case Ease.EaseInSine:
				return CreateInCurve(GetBezierKeyFrames(P(0.3619f, 0f), P(0.6739f, 0.4877f)));
			case Ease.EaseOutSine:
				return CreateOutCurve(GetBezierKeyFrames(P(0.3619f, 0f), P(0.6739f, 0.4877f)));
			case Ease.EaseInOutSine:
				return CreateInOutCurve(GetBezierKeyFrames(P(0.3619f, 0f), P(0.6739f, 0.4877f)));
			case Ease.EaseInExpo:
				return CreateInCurve(GetInExponentialKeyframes());
			case Ease.EaseOutExpo:
				return CreateOutCurve(GetInExponentialKeyframes());
			case Ease.EaseInOutExpo:
				return CreateInOutCurve(GetInExponentialKeyframes());
			case Ease.EaseInCirc:
				return CreateInCurve(GetBezierKeyFrames(P(0.5523f, 0f), P(0.9999f, 0.4477f)));
			case Ease.EaseOutCirc:
				return CreateOutCurve(GetBezierKeyFrames(P(0.5523f, 0f), P(0.9999f, 0.4477f)));
			case Ease.EaseInOutCirc:
				return CreateInOutCurve(GetBezierKeyFrames(P(0.5523f, 0f), P(0.9999f, 0.4477f)));
			case Ease.Linear:
				return CreateInCurve(GetBezierKeyFrames(P(0f, 0f), P(0f, 0f)));
			case Ease.Spring:
				return CreateInCurve(GetSpringKeyframes());
			case Ease.EaseInBounce:
				return CreateInCurve(GetInBounceKeyframes());
			case Ease.EaseOutBounce:
				return CreateOutCurve(GetInBounceKeyframes());
			case Ease.EaseInOutBounce:
				return CreateInOutCurve(GetInBounceKeyframes());
			case Ease.EaseInBack:
				return CreateInCurve(GetBezierKeyFrames(P(0.3333f, 0f), P(0.6666f, -0.567193f)));
			case Ease.EaseOutBack:
				return CreateOutCurve(GetBezierKeyFrames(P(0.3333f, 0f), P(0.6666f, -0.567193f)));
			case Ease.EaseInOutBack:
				return CreateInOutCurve(GetBezierKeyFrames(P(0.3333f, 0f), P(0.6666f, -0.86497f)));
			case Ease.EaseInElastic:
				return CreateInCurve(GetInElasticKeyframes());
			case Ease.EaseOutElastic:
				return CreateOutCurve(GetInElasticKeyframes());
			case Ease.EaseInOutElastic:
				return CreateInOutCurve(GetInElasticKeyframes());
			default:
				Debug.LogWarning("Can't find this curve. Returning linear.");
				return CreateInCurve(GetBezierKeyFrames(P(0f, 0f), P(0f, 0f)));
			}
		}

		private static AnimationCurve CreateInCurve(Keyframe[] inKeyframes)
		{
			return new AnimationCurve(inKeyframes);
		}

		private static AnimationCurve CreateOutCurve(Keyframe[] inKeyframes)
		{
			Keyframe[] array = new Keyframe[inKeyframes.Length];
			inKeyframes.CopyTo(array, 0);
			RotateKeyframes(array);
			return new AnimationCurve(array);
		}

		private static AnimationCurve CreateInOutCurve(Keyframe[] inKeyframes)
		{
			Keyframe[] array = new Keyframe[inKeyframes.Length];
			inKeyframes.CopyTo(array, 0);
			RotateKeyframes(array);
			Keyframe[] array2 = new Keyframe[inKeyframes.Length * 2];
			MoveAndScaleKeyframes(inKeyframes, Vector2.zero, 0.5f);
			MoveAndScaleKeyframes(array, Vector2.one * 0.5f, 0.5f);
			inKeyframes.CopyTo(array2, 0);
			array.CopyTo(array2, inKeyframes.Length);
			return new AnimationCurve(array2);
		}

		private static Keyframe[] GetInQuarticKeyframes()
		{
			Keyframe[] array = new Keyframe[3];
			for (int i = 0; i < array.Length; i++)
			{
				array[i].weightedMode = WeightedMode.Both;
			}
			array[0] = new Keyframe(0f, 0f, 0f, 0f, 0f, 0.35f);
			array[1] = new Keyframe(0.4023629f, 0.02591691f, 0.2671903f, 0.2671903f, 0.2314838f, 0.3768896f);
			array[2] = new Keyframe(1f, 1f, 4.165794f, 4.165794f, 0.2858976f, 0f);
			return array;
		}

		private static Keyframe[] GetInQuinticKeyframes()
		{
			Keyframe[] array = new Keyframe[3];
			for (int i = 0; i < array.Length; i++)
			{
				array[i].weightedMode = WeightedMode.Both;
			}
			array[0] = new Keyframe(0f, 0f, 0f, 0f, 0f, 0.5808958f);
			array[1] = new Keyframe(0.5020252f, 0.03216228f, 0.3849036f, 0.3389311f, 0.1664644f, 0.4185665f);
			array[2] = new Keyframe(1f, 1f, 4.973197f, 0f, 0.2820365f, 0f);
			return array;
		}

		private static Keyframe[] GetInExponentialKeyframes()
		{
			Keyframe[] array = new Keyframe[3];
			for (int i = 0; i < array.Length; i++)
			{
				array[i].weightedMode = WeightedMode.Both;
			}
			array[0] = new Keyframe(0f, 0f, 0f, 0.01230408f, 0f, 0.6417668f);
			array[1] = new Keyframe(0.5990406f, 0.06219158f, 0.424782f, 0.424782f, 0.1918493f, 0.5062984f);
			array[2] = new Keyframe(1f, 1f, 6.769706f, 0f, 0.2266997f, 0f);
			return array;
		}

		private static Keyframe[] GetInElasticKeyframes()
		{
			Keyframe[] array = new Keyframe[7];
			for (int i = 0; i < array.Length; i++)
			{
				array[i].weightedMode = WeightedMode.Both;
			}
			array[0] = new Keyframe(0f, 0f, 0f, 0f, 0.3333333f, 0.0943521f);
			array[1] = new Keyframe(0.2375961f, -0.004900184f, -0.09512027f, -0.09512027f, 0.4763626f, 0.4502789f);
			array[2] = new Keyframe(0.4253662f, 0.01661617f, -0.02957249f, -0.02957249f, 0.3228389f, 0.3695113f);
			array[3] = new Keyframe(0.5672264f, -0.04623402f, -0.01410099f, -0.01410099f, 0.3134277f, 0.4208294f);
			array[4] = new Keyframe(0.7112442f, 0.1319214f, 0.2673996f, 0.2673996f, 0.2878268f, 0.4619742f);
			array[5] = new Keyframe(0.8663773f, -0.3729638f, 0.01841709f, 0.01841709f, 0.3062344f, 0.4383868f);
			array[6] = new Keyframe(1f, 1f, 5.844434f, 0f, 0.2356896f, 0f);
			return array;
		}

		private static Keyframe[] GetInBounceKeyframes()
		{
			return GetBezierKeyFrames(P(0.01515f, 0.010415625f), P(0.030304547f, 1f / 64f), P(1f / 22f, 1f / 64f), P(0.060604546f, 1f / 64f), P(0.07575909f, 0.010415625f), P(1f / 11f, 0f), P(0.12120909f, 0.0416625f), P(0.15151818f, 0.0625f), P(0.18181819f, 0.0625f), P(0.2121182f, 0.0625f), P(0.24242727f, 0.0416625f), P(0.27272728f, 0f), P(0.3333273f, 0.16665f), P(0.39394546f, 0.25f), P(0.45454547f, 0.25f), P(0.5151455f, 0.25f), P(0.57576364f, 0.16665f), P(0.6363636f, 0f), P(0.75756365f, 0.6666f), P(0.8788f, 1f));
		}

		private static Keyframe[] GetSpringKeyframes()
		{
			Keyframe[] array = new Keyframe[5];
			for (int i = 0; i < array.Length; i++)
			{
				array[i].weightedMode = WeightedMode.Both;
			}
			array[0] = new Keyframe(0f, 0f, 0f, 4.198298f, 0f, 0.1125816f);
			array[1] = new Keyframe(0.580668f, 1.085675f, -0.01432603f, -0.01432603f, 0.3377516f, 0.3333333f);
			array[2] = new Keyframe(0.7965281f, 0.9724002f, 0.01279934f, 0.01279934f, 0.3085437f, 0.3732702f);
			array[3] = new Keyframe(0.9290999f, 1.008701f, 0.01008692f, 0.01008692f, 0.3721488f, 0.3323579f);
			array[4] = new Keyframe(1f, 1f, -0.016674f, 0f, 0.1967346f, 0f);
			return array;
		}

		private static Keyframe[] GetBezierKeyFrames(params Vector2[] points)
		{
			if (points.Length % 3 != 2 || points.Length < 2)
			{
				throw new ArgumentException("Need 3k + 2 control points, k >= 0. Thanks. :)");
			}
			int num = points.Length / 3;
			Keyframe[] array = new Keyframe[num + 2];
			for (int i = 0; i < array.Length; i++)
			{
				array[i].weightedMode = WeightedMode.Both;
			}
			if (num == 0)
			{
				UpdateSegment(ref array[0], ref array[1], Vector2.zero, points[0], points[1], Vector2.one);
			}
			else
			{
				UpdateSegment(ref array[0], ref array[1], Vector2.zero, points[0], points[1], points[2]);
				for (int j = 0; j < num - 1; j++)
				{
					UpdateSegment(ref array[j + 1], ref array[j + 2], points[j * 3 + 2], points[j * 3 + 3], points[j * 3 + 4], points[j * 3 + 5]);
				}
				UpdateSegment(ref array[num], ref array[num + 1], points[^3], points[^2], points[^1], Vector2.one);
			}
			return array;
		}

		private static void MoveAndScaleKeyframes(Keyframe[] keyframes, Vector2 move, float scale)
		{
			for (int i = 0; i < keyframes.Length; i++)
			{
				keyframes[i].time = move.x + keyframes[i].time * scale;
				keyframes[i].value = move.y + keyframes[i].value * scale;
			}
		}

		private static void RotateKeyframes(Keyframe[] keyframes)
		{
			Keyframe[] array = new Keyframe[keyframes.Length];
			for (int i = 0; i < keyframes.Length; i++)
			{
				Keyframe keyframe = keyframes[keyframes.Length - i - 1];
				array[i].time = 1f - keyframe.time;
				array[i].value = 1f - keyframe.value;
				array[i].inTangent = keyframe.outTangent;
				array[i].outTangent = keyframe.inTangent;
				array[i].inWeight = keyframe.outWeight;
				array[i].outWeight = keyframe.inWeight;
				array[i].weightedMode = WeightedMode.Both;
			}
			for (int j = 0; j < keyframes.Length; j++)
			{
				keyframes[j] = array[j];
			}
		}

		private static void UpdateSegment(ref Keyframe left, ref Keyframe right, Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3)
		{
			left.time = p0.x;
			left.value = p0.y;
			left.outTangent = GetTangent(p1 - p0);
			left.outWeight = ((p3.x == p0.x) ? 1f : ((p1.x - p0.x) / (p3.x - p0.x)));
			right.time = p3.x;
			right.value = p3.y;
			right.inTangent = GetTangent(p2 - p3);
			right.inWeight = ((p3.x == p0.x) ? 1f : ((p3.x - p2.x) / (p3.x - p0.x)));
		}

		private static float GetTangent(float x, float y)
		{
			if (x != 0f)
			{
				return y / x;
			}
			if (y > 0f)
			{
				return 1E+09f;
			}
			if (y < 0f)
			{
				return 1E+09f;
			}
			return 0f;
		}

		private static float GetTangent(Vector2 v)
		{
			return GetTangent(v.x, v.y);
		}

		private static Vector2 P(float x, float y)
		{
			return new Vector2(x, y);
		}
	}
}
